package com.njupt.dao;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.impl.CriteriaImpl;
import org.hibernate.transform.ResultTransformer;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.njupt.dao.PaginationSupport;

public class BaseDao extends HibernateDaoSupport{

	/**
	 * 保存对象。
	 * @param o
	 * @return
	 */
	public Object save(Object o){
		return this.getHibernateTemplate().merge(o); //merge(o)是保存
	}
	/**
	 * 删除对象。
	 * @param o
	 */
	public void delete(Object o){
		this.getHibernateTemplate().delete(o);
	}
	public void remove(Class entityClass,Serializable id){
		Object o = this.get(entityClass, id);
		this.getHibernateTemplate().delete(o);
	}
	/**
	 * 根据主键获取。
	 * @param entityClass
	 * @param id
	 * @return
	 */
	public Object get(Class entityClass,Serializable id){
		if(id == null)return null;
		return this.getHibernateTemplate().get(entityClass, id);
	}
	public List getAll(Class entityClass){
		return this.getHibernateTemplate().findByCriteria(DetachedCriteria.forClass(entityClass));
	}
	/**
	 * 执行查询HQL。
	 * @param hql
	 * @param params
	 * @return
	 */
	public List executeQuery(String hql, Object ... params){
		return this.getHibernateTemplate().find(hql, params);
	}
	/**
	 * 执行sql，但返回的List里不是POJO对象，而是Map对象。
	 * @param hql
	 * @param params
	 * @return
	 */
	public List executeQueryAsMap(final String sql, final Object ... params){
    	HibernateCallback callback = new HibernateCallback(){
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				Query q = session.createSQLQuery(sql);
				if(params != null){
					for (int i = 0; i < params.length; i++) {
						q.setParameter(i, params[i]);
					}
				}
				q.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP);
				List items = q.list();
				return items;
			}
    		
    	};
    	return (List) getHibernateTemplate().execute(callback);
	}
	public List executeQuery(String hql){
		return this.executeQuery(hql, null);
	}
	public int executeUpdate(String hql){
		return executeUpdate(hql,null);
	}
	/**
	 * 执行更新操作的hql 返回更新的条数。
	 * @param hql
	 * @param params
	 * @return
	 */
	public int executeUpdate(String hql, Object ... params){
		return this.getHibernateTemplate().bulkUpdate(hql, params);
	}
	/**
	 * 根据某个有唯一值的字段获取。
	 * @param persistentClass
	 * @param propertyName
	 * @param value
	 * @return
	 */
	public Object findUniqueBy(Class persistentClass,String propertyName, Object value) {
		Criteria criteria = getSession().createCriteria(persistentClass);
		return criteria.add(Restrictions.eq(propertyName, value)).uniqueResult();
	}
	/**
	 * QBC方式查询。
	 * @param detachedCriteria
	 * @return
	 */
	public List findByCriteria(
			final DetachedCriteria detachedCriteria) {
		return (List) getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException {
						Criteria criteria = detachedCriteria
								.getExecutableCriteria(session);
						List items = criteria.list();
						return items;
					}
				});
	}
	/**
	 * QBC方式，并且分页。
	 * @param detachedCriteria
	 * @param pageNo
	 * @param pageSize
	 * @return
	 */
	public PaginationSupport paginateByCriteria(DetachedCriteria detachedCriteria, int pageNo, int pageSize) {
		int startIndex = (pageNo-1)*pageSize;
		return findPageByCriteria(detachedCriteria, startIndex, pageSize);
	}
	public PaginationSupport findPageByCriteria(
			final DetachedCriteria detachedCriteria,
			final int startIndex, final int pageSize){
		return findPageByCriteria(detachedCriteria, startIndex, pageSize, null);
	}
	/**
	 * 注意这里参数的意义，startIndex，是起始记录序号而不是页码。
	 * 另外需要注意的是对于非lazy方式关联的对象使用Criteria执行的结果的内容。
	 * @param detachedCriteria
	 * @param startIndex
	 * @param pageSize
	 * @return
	 */
	public PaginationSupport findPageByCriteria(
			final DetachedCriteria detachedCriteria,
			final int startIndex, final int pageSize,
			final ResultTransformer resultTransformer) {
		return (PaginationSupport) getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException {
						int start = startIndex;
						CriteriaImpl criteria = (CriteriaImpl)detachedCriteria
								.getExecutableCriteria(session);
						//http://jdkcn.com/entry/16.html
						List orderEntries = null;
						List newEntrys = new ArrayList();
		                Field field = null;
		                try{
		                    field = CriteriaImpl.class.getDeclaredField("orderEntries");
		                    field.setAccessible(true);
		                    //Get orders
		                    orderEntries = (List) field.get(criteria);
		                    //Remove orders
		                    field.set(criteria,newEntrys);
		                }catch(Exception ex){
		                    ex.printStackTrace();
		                }
						Integer totalCount = ((Integer) criteria.setProjection(
								Projections.rowCount()).uniqueResult());
		                try{
		                    //Add orders return
		                	if(orderEntries != null)newEntrys.addAll(orderEntries);
		                }catch(Exception ex){
		                    ex.printStackTrace();
		                    //TODO cccc
		                }
						if(totalCount == null)totalCount=0;
						criteria.setProjection(null);
						if(start > totalCount && totalCount > 0){
							start = (totalCount - 1) / pageSize * pageSize;
						}
						if(resultTransformer != null)
							criteria.setResultTransformer(resultTransformer); 
						List items = criteria.setFirstResult(start)
								.setMaxResults(pageSize).list();
						PaginationSupport ps = new PaginationSupport(items,
								totalCount, start, pageSize);
						return ps;
					}
				});
	}
	/**
	 * 根据hql进行获取分页对象。
	 * @param hql
	 * @param pageNo
	 * @param pageSize
	 * @param params
	 * @return
	 */
	public PaginationSupport paginationByHql(final String hql,
			final int pageNo, final int pageSize,
			final Object... params){
		int startIndex = (pageNo-1)*pageSize;
		return findPageByHql(hql, null,
			startIndex, pageSize,
			params);
	}
	public PaginationSupport paginationByHql(final String hql,final String vCountHql,
			final int pageNo, final int pageSize,
			final Object... params){
		int startIndex = (pageNo-1)*pageSize;
		return findPageByHql(hql, vCountHql,
			startIndex, pageSize,
			params);
	}
	public PaginationSupport findPageByHql(final String hql,
			final int startIndex, final int pageSize,
			final Object... params){
		return findPageByHql(hql, null,
			startIndex, pageSize,
			params);
	}
	/**
	 * 注意这里参数的意义，startIndex，是起始记录序号而不是页码。
	 * 注意代码里的countHql的处理，对于有些情况可能会不适用。
	 * @param hql
	 * @param vCountHql
	 * @param startIndex
	 * @param pageSize
	 * @param params
	 * @return
	 */
	public PaginationSupport findPageByHql(final String hql,
			final String vCountHql, final int startIndex, final int pageSize,
			final Object... params) {
		return (PaginationSupport) getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException {
						Integer totalCount = 0;
						String countHql = vCountHql;
						if (countHql == null) {
							countHql = getCountHql(hql);
						}
						Number r = (Number) prepareQuery(session, countHql,
								params).uniqueResult();
						if(r != null)totalCount = r.intValue();
						if (totalCount == 0) {
							return new PaginationSupport(new ArrayList(), 0,
									startIndex, pageSize);
						}
						Query query = prepareQuery(session, hql, params);
						if (startIndex > 0) {
							query.setFirstResult(startIndex);
						}
						if (pageSize > 0) {
							query.setMaxResults(pageSize);
						}
						List items = query.list();
						PaginationSupport ps = new PaginationSupport(items,
								totalCount, startIndex, pageSize);
						return ps;
					}
				});
	}
	public List queryByHql(final String hql, final Integer startIndex, final Integer pageSize,
			final Object... params) {
		return (List) getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException {
						Query query = prepareQuery(session, hql, params);
						if (startIndex != null && startIndex > 0) {
							query.setFirstResult(startIndex);
						}
						if (pageSize != null && pageSize > 0) {
							query.setMaxResults(pageSize);
						}
						List items = query.list();
						return items;
					}
				});
	}
	public List queryByHql(final String hql,
			final Object... params) {
		return (List) getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException {
						Query query = prepareQuery(session, hql, params);
						List items = query.list();
						return items;
					}
				});
	}
	private Query prepareQuery(Session session, String hql, Object[] params) {
		Query query = session.createQuery(hql);
		if (params != null) {
			for (int i = 0; i < params.length; i++) {
				query.setParameter(i, params[i]);
			}
		}
		return query;
	}

	private String getCountHql(String hql) {
		int fromIndex = hql.toLowerCase().indexOf("from");
		String countHql = hql.substring(fromIndex);
		int orderByIndex = countHql.toLowerCase().indexOf("order by");
		if (orderByIndex != -1) {
			countHql = countHql.substring(0, orderByIndex);
		}
		return "select count(*) "+countHql;
	}

	public PaginationSupport findByExample(Object example, int startIndex, int pageSize){
		DetachedCriteria dc = DetachedCriteria.forClass(example.getClass());
		dc.add(Example.create(example).enableLike(MatchMode.ANYWHERE));
		return this.findPageByCriteria(dc, startIndex, pageSize);
	}
	/**
	 * http://blog.sina.com.cn/s/blog_55bba7c10100dkmk.html。
	 * @param nativeSql
	 * @param entityTypes
	 * @param params
	 * @return
	 */
	public List findByNativeSql(final String nativeSql, final Class[] entityTypes,final Object[] params){
		return findByNativeSql(nativeSql, null, entityTypes, params);
	}
	/**
	 * 执行本地sql。
	 * 
	 * @param nativeSql
	 * @param entityTypes
	 * @param params
	 * @return
	 */
	public List findByNativeSql(final String nativeSql,final String[] alias,final Class[] entityTypes,final Object[] params){
    	HibernateCallback callback = new HibernateCallback(){
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				SQLQuery q = session.createSQLQuery(nativeSql);
				if(params != null){
					for (int i = 0; i < params.length; i++) {
						q.setParameter(i, params[i]);
					}
				}
				if(entityTypes != null){
					for(int i = 0; i < entityTypes.length; i++){
						if(alias == null)
							q.addEntity(entityTypes[i]);
						else
							q.addEntity(alias[i],entityTypes[i]);
					}
				}
				return q.list();
			}
    		
    	};
    	return (List) getHibernateTemplate().execute(callback);
	}
	public List findByNativeSqlAsMap(final String nativeSql,final Object[] params){
    	HibernateCallback callback = new HibernateCallback(){
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				SQLQuery q = session.createSQLQuery(nativeSql);
				if(params != null){
					for (int i = 0; i < params.length; i++) {
						q.setParameter(i, params[i]);
					}
				}
				q.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP);
				return q.list();
			}
    		
    	};
    	return (List) getHibernateTemplate().execute(callback);
	}
	
	public PaginationSupport paginateByNativeSql(final String nativeSql,final Class[] entityTypes,final Object[] params,
			final int startIndex, final int pageSize
			){
		return paginateByNativeSql(nativeSql,null,entityTypes,params,startIndex,pageSize);
	}
	/**
	 * 分页。
	 * 
	 * @param nativeSql
	 * @param entities
	 * @param params
	 * @param startIndex
	 * @param pageSize
	 * @return
	 */
	public PaginationSupport paginateByNativeSql(final String nativeSql,final String[] alias,final Class[] entityTypes,final Object[] params,
			final int startIndex, final int pageSize
			){
    	HibernateCallback callback = new HibernateCallback(){
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				SQLQuery q = session.createSQLQuery("select count(*) from ("+nativeSql+") v");
				if(params != null){
					for (int i = 0; i < params.length; i++) {
						q.setParameter(i, params[i]);
					}
				}
				int totalCount = ((Number)q.uniqueResult()).intValue();
				q = session.createSQLQuery(nativeSql);
				if(params != null){
					for (int i = 0; i < params.length; i++) {
						q.setParameter(i, params[i]);
					}
				}
				if(entityTypes != null){
					for(int i = 0; i < entityTypes.length; i++){
						if(alias == null)
							q.addEntity(entityTypes[i]);
						else
							q.addEntity(alias[i],entityTypes[i]);
					}
				}
				q.setFirstResult(startIndex);
				q.setMaxResults(pageSize);
				List items = q.list();
				PaginationSupport ps = new PaginationSupport(items,
						totalCount, startIndex, pageSize);
				return ps;
			}
    		
    	};
    	return (PaginationSupport) getHibernateTemplate().execute(callback);
	}
	/**
	 * 传递nativeSql，将每条记录形成一个map。
	 * 
	 * @param nativeSql
	 * @param params
	 * @param startIndex
	 * @param pageSize
	 * @return
	 */
	public PaginationSupport paginateAsMapByNativeSql(final String nativeSql,final Object[] params,
			final int startIndex, final int pageSize
			){
    	HibernateCallback callback = new HibernateCallback(){
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				SQLQuery q = session.createSQLQuery("select count(*) from ("+nativeSql+") v");
				if(params != null){
					for (int i = 0; i < params.length; i++) {
						q.setParameter(i, params[i]);
					}
				}
				int totalCount = ((Number)q.uniqueResult()).intValue();
				q = session.createSQLQuery(nativeSql);
				if(params != null){
					for (int i = 0; i < params.length; i++) {
						q.setParameter(i, params[i]);
					}
				}
				q.setResultTransformer(Criteria.ALIAS_TO_ENTITY_MAP);
				q.setFirstResult(startIndex);
				q.setMaxResults(pageSize);
				List items = q.list();
				PaginationSupport ps = new PaginationSupport(items,
						totalCount, startIndex, pageSize);
				return ps;
			}
    		
    	};
    	return (PaginationSupport) getHibernateTemplate().execute(callback);
	}
	/**
	 * 执行修改数据的sql。
	 * @param nativeSql
	 * @param params
	 * @return
	 */
	public int executeUpdateByNativeSql(final String nativeSql,final Object[] params){
    	HibernateCallback callback = new HibernateCallback(){
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				SQLQuery q = session.createSQLQuery(nativeSql);
				if(params != null){
					for (int i = 0; i < params.length; i++) {
						q.setParameter(i, params[i]);
					}
				}
				return q.executeUpdate();
			}
    		
    	};
    	return (Integer) getHibernateTemplate().execute(callback);
	}
	/**
	 * 执行一个DetachedCriteria，获取单条数据。
	 * @param detachedCriteria
	 * @return
	 */
	public Object queryUniqueResultByCriteria(final DetachedCriteria detachedCriteria) {
		return getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException {
						Criteria criteria = detachedCriteria
								.getExecutableCriteria(session);
						criteria.setFirstResult(0);
						criteria.setMaxResults(1);
						return criteria.uniqueResult();
					}
				});
	}
    public List findByExample(Object o){
    	return getHibernateTemplate().findByExample(o);
    }
	public PaginationSupport findByExample(Object example, int start){
		DetachedCriteria dc = DetachedCriteria.forClass(example.getClass());
		dc.add(Example.create(example).enableLike(MatchMode.ANYWHERE));
		return this.findPageByCriteria(dc, start);
	}
	public <T> Criteria createCriteria(Class<T> entityClass, Criterion... criterions) {
		Criteria criteria = getSession().createCriteria(entityClass);
		for (Criterion c : criterions) {
			criteria.add(c);
		}
		return criteria;
	}
	public <T> Criteria createCriteria(String entityName, Criterion... criterions) {
		Criteria criteria = getSession().createCriteria(entityName);
		for (Criterion c : criterions) {
			criteria.add(c);
		}
		return criteria;
	}
	
	
	public int getCountByCriteria(final DetachedCriteria detachedCriteria) {
		Integer count = (Integer) getHibernateTemplate().executeWithNativeSession(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException {
						Criteria criteria = detachedCriteria
								.getExecutableCriteria(session);
						return criteria.setProjection(Projections.rowCount())
								.uniqueResult();
					}
				});
		return count.intValue();
	}
	public PaginationSupport findPageByCriteria(
			final DetachedCriteria detachedCriteria, final int startIndex) {
		return findPageByCriteria(detachedCriteria,
				startIndex, PaginationSupport.PAGESIZE);
	}
	public Object queryForUnique(final String hql,final  Object[] params){
		return getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session session)
							throws HibernateException {
						Query query = prepareQuery(session,hql,params);
						query.setFirstResult(0);
						query.setMaxResults(1);
						return query.uniqueResult();
					}
				}
				);
	}
}

